﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace DvtCap
{
    public enum Buffer_type
    {
        buffer_type_none,
        buffer_type_mjpg,
        buffer_type_h264,
        buffer_type_h265,
        buffer_type_rgb,
        buffer_type_rgba,
        buffer_type_yuy2,
        buffer_type_iyuv,
        buffer_type_i420,
        buffer_type_nv12
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct CamInfo
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
        public string name;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
        public string devpath;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct CamOption
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string buffertype;
        public int fps;           //表示每秒钟采样帧数
        public int width;          //表示采样分辨率	
        public int height;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct CamConfig
    {
        public CamOption stream0;
        public CamOption stream1;
    }

    public unsafe delegate void buffer_callback(byte* buf, int len, int buffer_type, int w, int h, double SampleTime, int streamidx, void* reserve0, void* reserve1);

    public class DvtCamWrapper
    {
        public const string LibraryName = "DvtCam.dll";
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private static extern int getDevicesCount();

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern void listDevices(byte[] list);

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern void* createCam(void* caminfo, buffer_callback cb, void* reserve0, void* reserve1);

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool getFormatCount(void* caminfo, int* major, int* minor0, int* minor1);

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern void listFormat(void* _cam, byte[] options);

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern int configCam(void* _cam, void* cam_config);

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern int connectDevice(void* _cam);

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool disconnectDevice(void* _cam);

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool releaseDevice(void* _cam);

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool setVideoSettingFilter(void* _cam, int Property, int lValue, int Flags = 2, bool useDefaultValue = false);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool setVideoSettingFilterPct(void* _cam, int Property, float pctValue, int Flags = 2);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool getVideoSettingFilter(void* _cam, int Property, int* min, int* max, int* SteppingDelta, int* currentValue, int* flags, int* defaultValue);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool getVideoSettingFilterAuto(void* _cam, int Property);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool setVideoSettingCamera(void* _cam, int Property, int lValue, int Flags = 2, bool useDefaultValue = false);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool setVideoSettingCameraPct(void* _cam, int Property, float pctValue, int Flags = 2);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool getVideoSettingCamera(void* _cam, int Property, int* min, int* max, int* SteppingDelta, int* currentValue, int* flags, int* defaultValue);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern bool getVideoSettingCameraAuto(void* _cam, int Property);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public unsafe static extern void convertYUY2toRGB24(void* pDst, void* pSrc, int w, int h);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public unsafe static extern void convertYUY2toBGR24(void* pDst, void* pSrc, int w, int h);
       
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public unsafe static extern void convertYUV420toBGR24(void* Y, void* U, void* V, void* dest,
            int width, int height, int Ystride, int UVstride, int RGBstride);
        
        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public unsafe static extern void convertNV12toBGR24(void* Y, void* dest,
            int width, int height, int Ystride, int UVstride, int RGBstride);

        [DllImport(LibraryName, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private unsafe static extern void* BuildTime();

        public static unsafe string BUILETIME()
        {
            return Marshal.PtrToStringAnsi(new IntPtr(BuildTime()));
        }

        static object bytesToStruct(byte[] bytes, Type type)
        {
            int size = Marshal.SizeOf(type);//Get size of the struct or class.          
            if (bytes.Length < size)
            {
                return null;
            }
            IntPtr structPtr = Marshal.AllocHGlobal(size);//Allocate memory space of the struct or class. 
            Marshal.Copy(bytes, 0, structPtr, size);//Copy byte array to the memory space.
            object obj = Marshal.PtrToStructure(structPtr, type);//Convert memory space to destination struct or class.         
            Marshal.FreeHGlobal(structPtr);//Release memory space.    
            return obj;
        }

        public unsafe static List<CamInfo> GetCameras()
        {
            int count = getDevicesCount();
            int size = Marshal.SizeOf(typeof(CamInfo));
            byte[] mem = new byte[count * size];
            listDevices(mem);
            List<CamInfo> camInfos = new List<CamInfo>();
            for (int inkIndex = 0; inkIndex < count; inkIndex++)
            {
                byte[] inkInfoBytes = new byte[size];
                Array.Copy(mem, inkIndex * size, inkInfoBytes, 0, size);
                CamInfo info = (CamInfo)bytesToStruct(inkInfoBytes, typeof(CamInfo));
                camInfos.Add(info);
            }
            return camInfos;
        }

        unsafe static void* CreateCamera(CamInfo info, buffer_callback cb, void* reserve0, void* reserve1)
        {
            int size = Marshal.SizeOf(typeof(CamInfo));
            IntPtr caminfo_mem = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(info, caminfo_mem, false);
            void*cam = createCam(caminfo_mem.ToPointer(), cb, reserve0, reserve1);
            Marshal.FreeHGlobal(caminfo_mem);
            return cam;
        }

        public unsafe List<List<CamOption>> ListFormat()
        {
            int major, minor0, minor1;
            if (getFormatCount(cam, &major, &minor0, &minor1))
            {
                int size = Marshal.SizeOf(typeof(CamOption));
                byte[] mem = new byte[(minor0 + minor1) * size];
                listFormat(cam, mem);
                List<List<CamOption>> camoptions = new List<List<CamOption>>();
                for (int i = 0; i < major; i++)
                {
                    camoptions.Add(new List<CamOption>());
                }
                for (int inkIndex = 0; inkIndex < minor0 + minor1; inkIndex++)
                {
                    byte[] inkInfoBytes = new byte[size];
                    Array.Copy(mem, inkIndex * size, inkInfoBytes, 0, size);
                    CamOption opt = (CamOption)bytesToStruct(inkInfoBytes, typeof(CamOption));
                    if (inkIndex < minor0)
                    {
                        camoptions[0].Add(opt);
                    }
                    else
                    {
                        camoptions[1].Add(opt);
                    }

                }
                return camoptions;
            }
            return null;
        }

        static unsafe void ConfigCam(void*cam,CamConfig config)
        {
            int size = Marshal.SizeOf(typeof(CamConfig));
            IntPtr p_config = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(config, p_config, false);
            configCam(cam, p_config.ToPointer());
            Marshal.FreeHGlobal(p_config);
        }

        public unsafe delegate void BufferCallBack(byte* buf, int len, int buffer_type, int w, int h, double SampleTime, int streamidx, void* reserve0, void* reserve1);

        public event BufferCallBack onBufferCallBack = null;

        unsafe void on_buffer_callback(byte* buf, int len, int buffer_type, int w, int h, double SampleTime, int streamidx, void* reserve0, void* reserve1)
        {
            //Console.WriteLine(len);
            onBufferCallBack?.Invoke(buf, len, buffer_type, w, h, SampleTime, streamidx, reserve0, reserve1);
        }

        //public static unsafe void test()
        //{
        //    var devices = GetCameras();
        //    if (devices.Count > 0)
        //    {
        //        buffer_callback buffer_Callback = new buffer_callback(on_buffer_callback);
        //        void* cam = CreateCamera(devices[0], buffer_Callback, null, null);
        //        var formats = ListFormat(cam);
        //        CamConfig config = new CamConfig();
        //        config.strema0 = formats[0][0];
        //        if (formats.Count == 2)
        //        {
        //            config.strema1 = formats[1][0];
        //        }
        //        ConfigCam(cam, config);
        //        connectDevice(cam);
        //        Thread.Sleep(10 * 1000);
        //        disconnectDevice(cam);
        //        releaseDevice(cam);
        //    }
        //}

        unsafe void* cam = null;
        CamInfo info;
        int streamCount = -1;
        buffer_callback buffer_Callback;
        CamConfig camConfig;
        public DvtCamWrapper(CamInfo _info)
        {
            this.info = _info;
        }

        ~DvtCamWrapper()
        {
            ReleaseCamera();
        }

        public unsafe void DisconnectCamera()
        {
            disconnectDevice(cam);
        }

        public unsafe void ConnectCamera()
        {
            buffer_Callback = new buffer_callback(on_buffer_callback);
            cam = CreateCamera(info, buffer_Callback, null, null);
        }

        public unsafe void ReleaseCamera()
        {
            DisconnectCamera();
            releaseDevice(cam);
            cam = null;
        }

        public unsafe bool OpenCamera(CamConfig _config)
        {
            camConfig = _config;
            ConfigCam(cam, _config);
            streamCount = connectDevice(cam);
            return streamCount > 0;
        }

        public CamInfo GetInfo()
        {
            return info;
        }

        public CamConfig GetConfig()
        {
            return camConfig;
        }

        public int GetStreamCount()
        {
            return streamCount;
        }

        public unsafe bool SetVideoSettingFilter(int Property, int lValue, int Flags, bool useDefaultValue)
        {
            return setVideoSettingFilter(cam, Property, lValue, Flags, useDefaultValue);
        }
        public unsafe bool SetVideoSettingFilterPct(int Property, float pctValue, int Flags)
        {
            return setVideoSettingFilterPct(cam, Property, pctValue, Flags);
        }
        public unsafe bool GetVideoSettingFilter(int Property, int* min, int* max, int* SteppingDelta, int* currentValue, int* flags, int* defaultValue)
        {
            //int _flag;
            bool ret = getVideoSettingFilter(cam, Property, min, max, SteppingDelta, currentValue, flags, defaultValue);
            //*flags = _flag;
            return ret;
        }
        public unsafe bool GetVideoSettingFilterAuto(int Property)
        {
            return getVideoSettingFilterAuto(cam, Property);
        }

        public unsafe bool SetVideoSettingCamera(int Property, int lValue, int Flags, bool useDefaultValue)
        {
            return setVideoSettingCamera(cam, Property, lValue, Flags, useDefaultValue);
        }
        public unsafe bool SetVideoSettingCameraPct(int Property, float pctValue, int Flags)
        {
            return setVideoSettingCameraPct(cam, Property, pctValue,Flags);
        }
        public unsafe bool GetVideoSettingCamera(int Property, int* min, int* max, int* SteppingDelta, int* currentValue, int* flags, int* defaultValue)
        {
            //int _flag;
            bool ret = getVideoSettingCamera(cam, Property, min, max, SteppingDelta, currentValue, flags, defaultValue);
            //*flags = PropertyFlags(_flag);
            return ret;
        }
        public unsafe bool GetVideoSettingCameraAuto(int Property)
        {
            return getVideoSettingCameraAuto(cam, Property);
        }

        public unsafe static void ConvertYUY2toRGB24(void* pDst, void* pSrc, int w, int h)
        {
            convertYUY2toRGB24(pDst, pSrc, w, h);
        }
        public unsafe static void ConvertYUY2toBGR24(void* pDst, void* pSrc, int w, int h)
        {
            convertYUY2toBGR24(pDst, pSrc, w, h);
        }

        public unsafe static void ConvertNV12toBGR24(void* pDst, int dstStride, void* pSrc, int w, int h)
        {
            convertNV12toBGR24(pSrc, pDst, w, h, w, w, dstStride);
        }
    }
}
